
#include "ToxMainWnd.h"
#include "../deskcore/ToxDeskApp.h"
#include "../deskcore/core/core.h"
#include "../deskcore/core/coreav.h"
#include "../deskcore/model/friend.h"
#include "../deskcore/friendlist.h"
#include "../deskcore/rfb/rfbproto.h"
#include "../deskcore/rfb/rfb.h"
#include <string>


#define WM_AVSTART WM_USER+1
#define WM_AVEND WM_USER+2

#define ID_TIMER_CON   1


ToxMainWnd::ToxMainWnd(QObject* parent)
// :conTimer(nullptr)
#ifdef _WIN32
    :m_inputInjector(false)
    #endif
{

}

QString ToxMainWnd::myId() const
{
	return myId_;
}

void ToxMainWnd::setMyId(const QString& id)
{
	myId_ = id;
    myIdChanged();
}

int ToxMainWnd::fid() const
{
	return fId;
}

void ToxMainWnd::setFid(const int fid)
{
	this->fId = fid;
}



int ToxMainWnd::conStatus() const
{
	return conStatus_;
}

void ToxMainWnd::setConStatus(const int status)
{
	conStatus_ = status;
}

int ToxMainWnd::avStatus() const
{
	return avStatus_;
}

void ToxMainWnd::setAvStatus(int status)
{
	avStatus_ = status;
}

void ToxMainWnd::startCon(const QString& toId)
{
	std::string stdId = toId.toStdString();
	auto toxKey =ToxDeskApp::getInstance().getToxKeyFromSimpleId(stdId);
	remoteId_ = QString::fromStdString(toxKey);
	conTimerId = startTimer(1000);
}

void ToxMainWnd::timerEvent(QTimerEvent* event)
{
	conTimerProc();
}

void ToxMainWnd::stopCon()
{
	killTimer(conTimerId);
}

void ToxMainWnd::init()
{
	auto core = ToxDeskApp::getCore();
    //myId_ = (core->getSelfId().toString().c_str());
    myId_ =ToxDeskApp::getInstance().getSimpleId().c_str();
    myIdChanged();
	onCoreChanged(*core);
}

void ToxMainWnd::onCoreChanged(Core& core)
{
	core.connected.connect(&ToxMainWnd::onConnected, this);
	core.disconnected.connect(&ToxMainWnd::onDisconnected, this);
	core.friendAdded.connect(&ToxMainWnd::addFriend, this);
	core.friendRequestReceived.connect(&ToxMainWnd::onFriendRequestReceived, this);
	core.friendDataReceived.connect(&ToxMainWnd::onFriendDataReceived, this);

	CoreAV* av = core.getAv();
	av->avInvite.connect(&ToxMainWnd::onAvInvite, this);
	av->avStart.connect(&ToxMainWnd::onAvStart, this);
	av->avEnd.connect(&ToxMainWnd::onAvEnd, this);
}

void ToxMainWnd::onConnected()
{
	conStatus_ = 1;
    conStatusChanged();
}

void ToxMainWnd::onDisconnected()
{
	conStatus_ = 0;
    conStatusChanged();
}

void ToxMainWnd::addFriend(uint32_t friendId, const ToxPk& friendPk)
{
	f = FriendList::addFriend(friendId, friendPk);
}

void ToxMainWnd::onFriendRequestReceived(const ToxPk& friendPk, const std::string& message)
{
	ToxDeskApp::getCore()->acceptFriendRequest(friendPk);
}

void ToxMainWnd::onFriendDataReceived(uint32_t friendnumber, const ToxString& message, bool isAction)
{
	const auto& friendId = FriendList::id2Key(friendnumber);
	Friend* f = FriendList::findFriend(friendId);
	if (!f) {
		return;
	}
	if (!isAction) {
		ReceiptNum  recNum;
		ToxDeskApp::getCore()->sendMessage(friendnumber, message.getstring(), recNum);
	}
	else {
	
		unsigned char* ch;
		std::vector<unsigned char> ba = message.getBytes();
		ch = ba.data();
		rfbPointerEventMsg* pe = (rfbPointerEventMsg*)ch;
        std::cout <<"type:"<<pe->type<< ",x:" << pe->x << ",y:" << pe->y << std::endl;
		if (pe->type == rfbPointerEvent) {
            setMouseEvent(pe->x, pe->y, pe->buttonMask);
		}
		else if (pe->type == rfbKeyEvent) {
			rfbKeyEventMsg* ke = (rfbKeyEventMsg*)ch;
            setKeyboardEvent(ke->key, ke->down==1);
		}
	}

}

void ToxMainWnd::onAvInvite(uint32_t friendId, bool video)
{
	auto core = ToxDeskApp::getCore();

	std::cout << "automatic call answer" << std::endl;
	CoreAV* coreav = core->getAv();
	coreav->answerCall(friendId, video);
}

void ToxMainWnd::onAvStart(uint32_t friendId, bool video)
{
	if (friendId != f->getId()) {
		return;
	}
	if (video) {
		//PostMessage(WM_AVSTART, friendId, 0);
		setFid(friendId);
        emit avStatusChanged();
	}
}

void ToxMainWnd::onAvEnd(uint32_t friendId, bool error)
{
	//PostMessage(WM_AVEND, friendId, 0);

}



void ToxMainWnd::conTimerProc()
{
	connectCount++;

	if (!remoteId_.isEmpty()) {
		ToxId tokid(remoteId_.toStdString());
		bool bhave = ToxDeskApp::getCore()->hasFriendWithPublicKey(tokid.getPublicKey());
		if (bhave) {
			f = FriendList::findFriend(tokid.getPublicKey());
			if (f) {
				auto core = ToxDeskApp::getCore();

				CoreAV* av = core->getAv();
				uint32_t friendId = f->getId();
				if (av->isCallStarted(f)) {
					// TODO: We want to activate video on the active call.
					if (av->isCallVideoEnabled(f)) {
						av->cancelCall(friendId);
					}
				}
				else if (av->startCall(friendId, true)) {
					bConnecting = false;
					//KillTimer(GetHWND(), ID_TIMER_CON);
					//conTimer->stop();
					killTimer(conTimerId);
					conStatus_ = 1;
				}
			}
		}
		else {
			ToxDeskApp::getCore()->requestFriendship(tokid, "con");
		}
	}
}

void ToxMainWnd::setKeyboardEvent(UINT32 keySym, bool down)
{
#ifdef _WIN32
        // Generate single key event.
        BYTE vkCode;
        WCHAR ch;
        bool release = !down;
        bool extended;

        if (m_keyMap.keySymToVirtualCode(keySym, &vkCode, &extended)) {
            m_inputInjector.injectKeyEvent(vkCode, release, extended);
        }
        else if (m_keyMap.keySymToUnicodeChar(keySym, &ch)) {
            m_inputInjector.injectCharEvent(ch, release);
        }
#endif

}
void ToxMainWnd::setMouseEvent(int x, int y, unsigned  char keyFlag)
{
#ifdef _WIN32
    DWORD dwFlags = 0;
    dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;

    bool prevState;
    bool currState;
    // Check the left button on change state
    prevState = (m_prevKeyFlag & 1) != 0;
    currState = (keyFlag & 1) != 0;
    if (currState != prevState) {
        if (currState) {
            dwFlags |= MOUSEEVENTF_LEFTDOWN;
        }
        else {
            dwFlags |= MOUSEEVENTF_LEFTUP;
        }
    }
    // Check the middle button on change state
    prevState = (m_prevKeyFlag & 2) != 0;
    currState = (keyFlag & 2) != 0;
    if (currState != prevState) {
        if (currState) {
            dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
        }
        else {
            dwFlags |= MOUSEEVENTF_MIDDLEUP;
        }
    }
    // Check the right button on change state
    prevState = (m_prevKeyFlag & 4) != 0;
    currState = (keyFlag & 4) != 0;
    if (currState != prevState) {
        if (currState) {
            dwFlags |= MOUSEEVENTF_RIGHTDOWN;
        }
        else {
            dwFlags |= MOUSEEVENTF_RIGHTUP;
        }
    }

    // Check on a mouse wheel
    DWORD mouseWheelValue = 0;
    bool prevWheelUp = (m_prevKeyFlag & 8) != 0;
    bool currWheelUp = (keyFlag & 8) != 0;
    bool prevWheelDown = (m_prevKeyFlag & 16) != 0;
    bool currWheelDown = (keyFlag & 16) != 0;

    if (currWheelUp && !prevWheelUp) {
        dwFlags |= MOUSEEVENTF_WHEEL;
        mouseWheelValue = 120;
    }
    else if (currWheelDown && !prevWheelDown) {
        dwFlags |= MOUSEEVENTF_WHEEL;
        mouseWheelValue = -120;
    }

    m_prevKeyFlag = keyFlag;

    // Normilize pointer position
    UINT16 desktopWidth = GetSystemMetrics(SM_CXSCREEN);
    UINT16 desktopHeight = GetSystemMetrics(SM_CYSCREEN);
    int fbOffsetX = GetSystemMetrics(SM_XVIRTUALSCREEN);
    int fbOffsetY = GetSystemMetrics(SM_YVIRTUALSCREEN);
    x = (INT32)((x + fbOffsetX) * 65535 / (desktopWidth - 1));
    y = (INT32)((y + fbOffsetY) * 65535 / (desktopHeight - 1));

    INPUT input;
    memset(&input, 0, sizeof(INPUT));
    input.type = INPUT_MOUSE;
    input.mi.dwFlags = dwFlags;
    input.mi.dx = x;
    input.mi.dy = y;
    input.mi.mouseData = mouseWheelValue;
    SendInput(1, &input, sizeof(INPUT));
 #endif
}
